Appearance
《JAVA应用开发》期末试卷A (精选01)
一、辨析题(对的直接打 $\surd$ ,错的打×并改正过来。每小题 2 分,共 20 分)
- $1$ 是 java语言中不合法的标识符。( )
查看答案与解析
答案:$\surd$
解析: Java 标识符的命名规则为:由字母、数字、下划线 _ 和美元符号 $ 组成,且不能以数字开头。纯数字 1 以数字开头,不能作为合法的 Java 标识符。因此,题目表述"$1$ 是 java语言中不合法的标识符"是正确的,应打 $\surd$。
难度: ⭐ 考点: #Java标识符
💡 学习锦囊
📖 相关公式与知识点:
- 标识符规则:由字母、数字、
_、$组成,不能以数字开头,不能是关键字,区分大小写。
易错点
- 标识符不能以数字开头,但可以包含数字(如
a1合法,1a不合法)。
🔄 举一反三
- 判断:
_value是 Java 中合法的标识符。( )查看练习答案与解析
答案:$\surd$解析:以下划线
_开头是合法的 Java 标识符。
- java基本数据类型中,boolean不能与其他类型相互转换。( )
查看答案与解析
答案:$\surd$
解析: Java 中 boolean 类型只有两个值:true 和 false。boolean 类型不能与 int、float、char 等其他基本数据类型进行相互转换。在 C/C++ 中,0 表示 false,非 0 表示 true,但 Java 是强类型语言,boolean 与其他类型之间不存在任何隐式或显式的类型转换关系。
难度: ⭐ 考点: #boolean类型 #类型转换
💡 学习锦囊
📖 相关公式与知识点:
- boolean 类型:Java 的基本数据类型,只有
true和false两个值,不能与数值类型互转。 - 与 C/C++ 的区别:C 语言中 0 表示 false、非 0 表示 true,Java 严格区分。
易错点
- 不能用
if (1)这样的写法,Java 要求条件表达式必须是 boolean 类型。
🔄 举一反三
- 判断:Java 中
boolean b = 1;是合法的。( )查看练习答案与解析
答案:× 解析:Java 中不能将 int 类型的 1 直接赋给 boolean 变量,必须使用
true或false。
- java中的字符类型采用的是 Unicode编码,每个字符占32 个比特位。( )
查看答案与解析
答案:×
改正: java中的字符类型采用的是 Unicode编码,每个字符占 16 个比特位。
解析: Java 的 char 类型采用 UTF-16 编码表示 Unicode 字符,每个 char 占 2 个字节,即 16 个比特位(bit),取值范围为 \u0000 ~ \uffff(0~65535)。32 个比特位是 4 个字节,那是 int 类型的大小。
难度: ⭐ 考点: #char类型 #Unicode编码
💡 学习锦囊
📖 相关公式与知识点:
- char 类型:占 2 字节(16 位),采用 UTF-16 编码,取值范围 0~65535。
- 各类型大小:byte(1B), short(2B), int(4B), long(8B), float(4B), double(8B), char(2B)。
易错点
- char 是 16 位(2 字节),不是 32 位。32 位是 int 类型的大小。
🔄 举一反三
- 判断:Java 中 int 类型占 4 个字节,即 32 个比特位。( )
查看练习答案与解析
答案:$\surd$解析:int 占 4 字节(32 位),有符号整数,取值范围为 $-2^{31}$ 到 $2^{31}-1$。
- java 源文件编译后将产生相应的字节码文件,其扩展名为.byte。( )
查看答案与解析
答案:×
改正: java 源文件编译后将产生相应的字节码文件,其扩展名为 .class。
解析: Java 源文件(.java)经过 javac 编译器编译后,生成的是字节码文件(.class),而非 .byte。.class 文件由 Java 虚拟机(JVM)解释执行。
难度: ⭐ 考点: #字节码文件 #编译流程
💡 学习锦囊
📖 相关公式与知识点:
- 编译流程:
.java→javac→.class(字节码)→java→ JVM 执行。 - javac:Java 编译器命令。
- java:Java 虚拟机启动命令。
易错点
- 编译后扩展名是
.class,不是.byte。
🔄 举一反三
- 判断:运行 Java 字节码文件 Test.class 的命令是
java Test.class。( )查看练习答案与解析
答案:× 解析:运行时应使用
java Test,不加.class后缀。
- 在java中,除了4 类8种基本数据类型以外,其余都是引用类型。( )
查看答案与解析
答案:$\surd$
解析: Java 的数据类型分为两大类:基本数据类型(Primitive Type)和引用数据类型(Reference Type)。基本数据类型共 4 类 8 种:整数型(byte, short, int, long)、浮点型(float, double)、字符型(char)、布尔型(boolean)。除此之外,所有的类、接口、数组、枚举等都是引用类型。
难度: ⭐ 考点: #基本数据类型 #引用类型
💡 学习锦囊
📖 相关公式与知识点:
- 8 种基本数据类型:byte(1B), short(2B), int(4B), long(8B), float(4B), double(8B), char(2B), boolean。
- 引用类型:类、接口、数组、枚举、注解等。
易错点
- String 是引用类型,不是基本数据类型。
- Integer、Double 等包装类也是引用类型。
🔄 举一反三
- 判断:Java 中数组是基本数据类型。( )
查看练习答案与解析
答案:× 解析:数组在 Java 中属于引用类型,通过
new关键字在堆内存中创建。
- 在java中,一个父类可以有多个子类,一个子类也可以有多个父类。( )
查看答案与解析
答案:×
改正: 在java中,一个父类可以有多个子类,一个子类只能有一个直接父类(Java 不支持多继承)。
解析: Java 中类之间的继承是单继承的,即一个子类只能有一个直接父类(使用 extends 关键字)。但一个父类可以被多个子类继承。Java 通过接口(implements)来实现多继承的效果,一个类可以实现多个接口。
难度: ⭐ 考点: #单继承 #接口多实现
💡 学习锦囊
📖 相关公式与知识点:
- 单继承:一个类只能 extends 一个父类,但可以 implements 多个接口。
- 接口多继承:一个接口可以 extends 多个接口。
易错点
- Java 不支持类的多继承,只支持接口的多实现。
🔄 举一反三
- 判断:Java 中一个接口可以继承多个接口。( )
查看练习答案与解析
答案:$\surd$解析:接口支持多继承,如
interface C extends A, B {}。
- 构造方法的主要作用是完成对类的对象的初始化工作,其返回类型只能是 void。( )
查看答案与解析
答案:×
改正: 构造方法的主要作用是完成对类的对象的初始化工作,构造方法没有返回类型(连 void 也不能写)。
解析: 构造方法是一种特殊的方法,它没有返回值类型声明——既不是 void,也不是其他任何类型。如果在构造方法前加了 void,它就变成了一个普通的成员方法,而不再是构造方法。
难度: ⭐ 考点: #构造方法
💡 学习锦囊
📖 相关公式与知识点:
- 构造方法:无返回类型,方法名与类名相同,用于初始化对象。
- 默认构造方法:如果类中没有定义任何构造方法,编译器会自动生成无参构造方法。
易错点
- 构造方法不能写 void,写了 void 就变成了普通方法。
🔄 举一反三
- 判断:一个类可以有多个构造方法。( )
查看练习答案与解析
答案:$\surd$解析:构造方法支持重载,一个类可以定义多个参数列表不同的构造方法。
- 在 java 类中,可以用来声明成员访问权限的修饰符有 public、protected 和 private。( )
查看答案与解析
答案:$\surd$
解析: Java 中成员访问权限修饰符共有 4 种:public(公共)、protected(受保护)、默认(不写,包级访问)和 private(私有)。题目中列出了 public、protected 和 private 三种,这三种确实都是可以用来声明成员访问权限的修饰符。虽然漏掉了默认访问权限,但题目说的是"可以用来声明",而非"全部",所以该说法正确。
难度: ⭐ 考点: #访问修饰符
💡 学习锦囊
📖 相关公式与知识点:
- 访问修饰符(权限从大到小):public > protected > default > private。
- public:所有类可访问。
- protected:本包 + 其他包子类可访问。
- default(不写):本包可访问。
- private:仅本类可访问。
易错点
- 除了 public、protected、private,还有默认访问权限(不写任何修饰符)。
🔄 举一反三
- 判断:用 private 修饰的成员变量可以被子类直接访问。( )
查看练习答案与解析
答案:× 解析:private 成员只能在本类中访问,子类也不能直接访问。
- 字符串 String 类中使用 substring 方法来查找该字符串中是否包含某个子串。( )
查看答案与解析
答案:×
改正: 字符串 String 类中使用 contains 方法(或 indexOf 方法)来查找该字符串中是否包含某个子串。substring 方法用于截取子字符串。
解析:
substring(int beginIndex)/substring(int beginIndex, int endIndex):用于从字符串中截取子串。contains(CharSequence s):用于判断字符串中是否包含指定的字符序列,返回 boolean。indexOf(String str):返回指定子串第一次出现的索引,若不存在则返回 -1。
难度: ⭐ 考点: #String类 #substring #contains
💡 学习锦囊
📖 相关公式与知识点:
- String 常用方法:
length(),charAt(),substring(),indexOf(),contains(),equals(),toUpperCase()等。 - substring:截取子串。
- contains/indexOf:查找子串。
易错点
- substring 是截取子串,contains/indexOf 才是查找子串。
🔄 举一反三
- 判断:
"Hello".substring(1, 3)的结果是"el"。( )查看练习答案与解析
答案:$\surd$解析:
substring(1, 3)截取索引 1(包含)到索引 3(不包含)的子串,即"el"。
- java中的接口是抽象方法和常量值的集合。( )
查看答案与解析
答案:$\surd$
解析: Java 接口(interface)中声明的变量默认是 public static final 的,即常量;声明的方法默认是 public abstract 的,即抽象方法(Java 8 之后也允许 default 方法和 static 方法,但核心本质仍是抽象方法和常量的集合)。因此该说法正确。
难度: ⭐ 考点: #接口
💡 学习锦囊
📖 相关公式与知识点:
- 接口:变量默认
public static final(常量),方法默认public abstract(抽象方法)。 - Java 8+:接口中还可以定义
default方法和static方法(有方法体)。
易错点
- 接口不能直接实例化(new),但可以通过匿名内部类创建。
🔄 举一反三
- 判断:Java 中可以使用
new关键字创建接口的实例。( )查看练习答案与解析
答案:× 解析:接口不能直接实例化,但可以通过匿名内部类的方式创建接口的实例(本质是创建了一个实现了该接口的匿名类的对象)。
二、简答题(每小题 6 分,共 30 分)
- 简述 StringBuffer 类的常用方法及其与 String 类的区别。
查看答案与解析
答案:
StringBuffer 常用方法:
append(String s):在末尾追加字符串。insert(int offset, String s):在指定位置插入字符串。delete(int start, int end):删除指定范围的字符。reverse():反转字符串。replace(int start, int end, String s):替换指定范围的字符。toString():转换为 String 对象。length()/capacity():获取长度 / 容量。
StringBuffer 与 String 的区别:
| 比较维度 | String | StringBuffer |
|---|---|---|
| 可变性 | 不可变(immutable) | 可变(mutable) |
| 线程安全 | 安全(不可变天然安全) | 安全(方法用 synchronized 修饰) |
| 性能 | 频繁拼接会产生大量临时对象 | 频繁拼接性能更高 |
| 存储 | 字符串常量池 | 堆内存 |
难度: ⭐⭐ 考点: #StringBuffer #String
💡 学习锦囊
📖 相关公式与知识点:
- String vs StringBuffer vs StringBuilder:String 不可变,StringBuffer 线程安全可变,StringBuilder 非线程安全可变(性能最高)。
- StringBuffer 常用方法:append, insert, delete, reverse, replace, toString。
易错点
- StringBuffer 和 StringBuilder 的区别常被混淆(前者线程安全,后者不是)。
🔄 举一反三
- 简述 StringBuilder 与 StringBuffer 的区别。
查看练习答案与解析
答案:
- StringBuffer:线程安全,方法使用
synchronized修饰,适用于多线程环境,性能略低。 - StringBuilder:非线程安全,方法没有同步,适用于单线程环境,性能更高。
- 两者 API 几乎完全相同,都继承自
AbstractStringBuilder。
- StringBuffer:线程安全,方法使用
- 什么是servlet?常用的开发方式有哪些?
查看答案与解析
答案:
Servlet 定义: Servlet 是运行在 Web 服务器或应用服务器上的 Java 程序,用于处理客户端(浏览器)的 HTTP 请求并生成响应。它是 Java EE 规范中的核心组件,充当 Web 应用的控制层。
常用开发方式:
- 实现 Servlet 接口:直接实现
javax.servlet.Servlet接口,需实现所有方法(init, service, destroy 等)。 - 继承 GenericServlet:继承
javax.servlet.GenericServlet抽象类,只需重写service()方法,协议无关。 - 继承 HttpServlet(最常用):继承
javax.servlet.http.HttpServlet,根据请求方式重写doGet(),doPost(),doPut(),doDelete()等方法。 - 注解配置:使用
@WebServlet注解替代 web.xml 配置。
难度: ⭐⭐ 考点: #Servlet
💡 学习锦囊
📖 相关公式与知识点:
- Servlet 生命周期:加载 → 实例化 → init() → service() → doGet/doPost() → destroy()。
- HttpServlet:最常用的 Servlet 开发方式,重写 doGet/doPost 方法。
易错点
- Servlet 是服务器端程序,不是客户端程序。
🔄 举一反三
- 简述 Servlet 的生命周期。
查看练习答案与解析
答案:
- 加载和实例化:Servlet 容器加载 Servlet 类并创建实例。
- 初始化:调用
init()方法,只执行一次。 - 处理请求:调用
service()方法,根据请求类型分发到 doGet/doPost 等。 - 销毁:调用
destroy()方法,释放资源。
- 简述请求转发和请求重定向的区别。
查看答案与解析
答案:
请求转发(Forward)与请求重定向(Redirect)的区别:
| 比较维度 | 请求转发 (Forward) | 请求重定向 (Redirect) |
|---|---|---|
| 请求次数 | 1 次请求 | 2 次请求 |
| 地址栏 | 不变(显示原 URL) | 改变(显示新 URL) |
| 数据共享 | 同一个 request,可共享数据 | 不同 request,不能共享数据 |
| 跳转范围 | 仅限服务器内部 | 可跳转到外部 URL |
| 实现方式 | request.getRequestDispatcher().forward() | response.sendRedirect() |
| 速度 | 较快 | 较慢(多一次网络往返) |
难度: ⭐⭐ 考点: #请求转发 #请求重定向
💡 学习锦囊
📖 相关公式与知识点:
- 转发:服务器内部行为(
request.getRequestDispatcher),一次请求,地址栏不变。 - 重定向:让浏览器重新发请求(
response.sendRedirect),两次请求,地址栏改变。
易错点
- 请求转发地址栏不变,重定向地址栏改变——这是最直观的区分方式。
🔄 举一反三
- 在 Servlet 中,如何将请求转发到
target.jsp?查看练习答案与解析
答案:
request.getRequestDispatcher("target.jsp").forward(request, response);解析:通过 RequestDispatcher 的 forward 方法实现服务器内部转发。
- 简述至少两种防止SQL注入漏洞的常用方法。
查看答案与解析
答案:
防止 SQL 注入的常用方法:
使用 PreparedStatement(预编译语句):
- 将 SQL 语句与参数分离,参数使用占位符
?。 - 数据库会预先编译 SQL 模板,参数值不会被当作 SQL 代码执行。
- 示例:
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name=?"); ps.setString(1, userName);
- 将 SQL 语句与参数分离,参数使用占位符
输入验证与过滤:
- 对用户输入进行严格的格式校验(如正则表达式)。
- 过滤或转义特殊字符(如单引号
'、分号;、注释符--等)。
使用 ORM 框架(如 Hibernate、MyBatis):
- ORM 框架内部通常使用参数化查询,能有效防止 SQL 注入。
最小权限原则:
- 数据库用户只授予必要的权限,避免使用 root/sa 等高权限账户连接数据库。
难度: ⭐⭐ 考点: #SQL注入 #PreparedStatement
💡 学习锦囊
📖 相关公式与知识点:
- SQL 注入原理:攻击者通过拼接恶意 SQL 代码来操纵数据库。核心防御是参数化查询。
- PreparedStatement:预编译 SQL,参数使用
?占位符,通过setXxx()方法设置参数值。
易错点
- PreparedStatement 是防 SQL 注入的核心手段,不要只说"过滤特殊字符"。
🔄 举一反三
- 以下代码是否存在 SQL 注入风险?
Statement stmt = conn.createStatement(); stmt.executeQuery("SELECT * FROM users WHERE name='" + userName + "'");查看练习答案与解析
答案:存在风险。 解析:使用 Statement 拼接字符串的方式构建 SQL,如果 userName 包含恶意代码(如
' OR '1'='1),会导致 SQL 注入。应改用 PreparedStatement。
- 简述http协议中至少 3种常用的请求头。
查看答案与解析
答案:
HTTP 协议常用请求头(至少 3 种):
- Host:指定请求的服务器域名和端口号(HTTP/1.1 必须)。例如:
Host: www.example.com - User-Agent:标识客户端浏览器及操作系统信息。例如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) - Content-Type:指定请求体的 MIME 类型。例如:
Content-Type: application/json或application/x-www-form-urlencoded - Accept:告知服务器客户端能处理的媒体类型。例如:
Accept: text/html, application/json - Cookie:携带客户端的 Cookie 信息发送给服务器。例如:
Cookie: JSESSIONID=abc123 - Authorization:携带认证凭证信息。例如:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - Referer:指明请求来源页面的 URL。
难度: ⭐⭐ 考点: #HTTP协议 #请求头
💡 学习锦囊
📖 相关公式与知识点:
- HTTP 请求结构:请求行 + 请求头 + 空行 + 请求体。
- 常见请求头:Host, User-Agent, Content-Type, Accept, Cookie, Authorization, Referer。
易错点
- Host 是 HTTP/1.1 必须的请求头,不能省略。
🔄 举一反三
- 简述 GET 请求和 POST 请求的区别。
查看练习答案与解析
答案:
- GET:参数拼接在 URL 后面,有长度限制(约 2KB),数据可见,适合查询操作,可被缓存和收藏。
- POST:参数放在请求体中,无长度限制,数据不可见,适合提交/修改操作,不可缓存。
- GET 是幂等的(多次请求结果相同),POST 不是幂等的。
三、程序填空题(每空 3分,共 30 分)
- 下面的程序对"百鸡百钱"问题进行了求解,公鸡每只 3 元,母鸡每只 5 元,小鸡 3只 1元,用 100元钱买100只鸡,公鸡、母鸡、小鸡应各买多少?请将程序填写完整。
public class Blank1 {
public static void main(String[] args) {
int a, b, c;
for (a = 0; ______; a++) {
for (b = 0; ______; b++) {
c = 100 - a - b;
if ((3 * a + 5 * b + c / 3 == 100) && (______))
System.out.println("公鸡:" + a + "母鸡:" + b + "小鸡:" + c);
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
查看答案与解析
答案:
- 空1:
a <= 33(或a < 34,因为公鸡最多买 100/3 ≈ 33 只) - 空2:
b <= 20(或b < 21,因为母鸡最多买 100/5 = 20 只) - 空3:
c % 3 == 0(小鸡 3 只 1 元,所以小鸡数量必须是 3 的倍数)
解析: "百鸡百钱"问题:公鸡 3 元/只,母鸡 5 元/只,小鸡 3 只/1 元(即 1/3 元/只)。100 元买 100 只鸡。
设公鸡 a 只,母鸡 b 只,小鸡 c 只,则:
- $a + b + c = 100$(数量约束)
- $3a + 5b + c/3 = 100$(金额约束)
由第一个等式得 $c = 100 - a - b$,代入第二个等式。
公鸡最多买 $\lfloor 100/3 \rfloor = 33$ 只,母鸡最多买 $\lfloor 100/5 \rfloor = 20$ 只。
由于小鸡 3 只 1 元,c 必须是 3 的倍数,即 c % 3 == 0。
难度: ⭐⭐ 考点: #百鸡百钱 #穷举法
💡 学习锦囊
📖 相关公式与知识点:
- 百鸡百钱:经典的不定方程问题,通过穷举法(枚举)求解,利用约束条件缩小搜索范围。
- 枚举优化:利用约束条件缩小循环范围,公鸡最多 33 只,母鸡最多 20 只。
易错点
- 百鸡百钱中容易忘记
c % 3 == 0这个条件,导致出现非整数的小鸡数量。
🔄 举一反三
- 如果公鸡 5 元/只,母鸡 3 元/只,小鸡 1/3 元/只,100 元买 100 只鸡,公鸡最多买多少只?
查看练习答案与解析
答案:20 只 解析:$\lfloor 100/5 \rfloor = 20$,公鸡最多买 20 只。
- 请根据注释提示将下面的程序填写完整。
public class Blank2 {
public static void main(String args[]) {
String s1 = new String("2012");
String s2 = new String("100.50");
int x = ______; //将s1转换为int类型
double y = ______; //将s2转换为double类型
double z = x + y;
String s3 = ______; //将z转换为字符串
StringBuffer sbr = new StringBuffer("Thingking");
String s4 = new String("in Java");
______; //将s4连接在sbr的后面
System.out.println(sbr.toString()); //显示为Thingking in Java
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
查看答案与解析
答案:
- 空1:
Integer.parseInt(s1) - 空2:
Double.parseDouble(s2) - 空3:
String.valueOf(z)(或Double.toString(z)或"" + z) - 空4:
sbr.append(s4)
解析:
Integer.parseInt(String s):将字符串解析为 int 类型。Double.parseDouble(String s):将字符串解析为 double 类型。String.valueOf(double d):将 double 转换为 String。StringBuffer.append(String s):在 StringBuffer 末尾追加字符串,直接修改原对象。
难度: ⭐⭐ 考点: #类型转换 #Integer.parseInt #Double.parseDouble #StringBuffer
💡 学习锦囊
📖 相关公式与知识点:
- 类型转换:
- 字符串 → 基本类型:
Integer.parseInt(),Double.parseDouble(),Boolean.parseBoolean()等。 - 基本类型 → 字符串:
String.valueOf(),Integer.toString(), 或使用"" + 数值。
- 字符串 → 基本类型:
- StringBuffer.append():直接在原对象上修改,返回 this,支持链式调用。
易错点
parseInt和parseDouble是静态方法,通过类名调用。- StringBuffer 的 append 直接修改原对象,不需要接收返回值。
🔄 举一反三
- 将字符串
"3.14"转换为 float 类型,应使用什么方法?查看练习答案与解析
答案:
Float.parseFloat("3.14")解析:与 Integer.parseInt 和 Double.parseDouble 类似,Float 类也提供了parseFloat(String s)静态方法。
- 下面的程序是采用冒泡法对数组元素按从小到大的顺序排序,请将程序填写完整。
public class Blank3 {
public static void main(String[] args) {
int[] a = new int[]{21, 34, 211, 15, 92, 68, 89, 794, 11, 863};
int temp;
for (int i = 0; i < 10; i++)
for (int j = 0; j < ______; j++)
if (a[j] > a[j + 1]) {
temp = a[j];
______;
______;
}
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
查看答案与解析
答案:
- 空1:
9 - i(或a.length - 1 - i) - 空2:
a[j] = a[j + 1] - 空3:
a[j + 1] = temp
解析: 冒泡排序的核心思想:每一轮将当前未排序部分的最大值"冒泡"到最后。
- 外层循环控制轮数(共 n-1 轮)。
- 内层循环进行相邻元素比较交换,每轮比较范围递减(因为每轮结束后最后 i 个元素已排好)。
- 第 i 轮时,需要比较前
n - 1 - i对相邻元素,即j < 9 - i(n=10)。 - 交换三部曲:temp 暂存 → 赋值 → 赋值。
难度: ⭐⭐ 考点: #冒泡排序
💡 学习锦囊
📖 相关公式与知识点:
- 冒泡排序:时间复杂度 $O(n^2)$,空间复杂度 $O(1)$,稳定排序。每轮将最大元素移到末尾。
- 交换三部曲:temp = a[j]; a[j] = a[j+1]; a[j+1] = temp;
易错点
- 冒泡排序内层循环边界是
n - 1 - i,不是n - 1。
🔄 举一反三
- 用冒泡排序对数组
{5, 2, 8, 1, 9}按从大到小排序,写出内层 if 条件。查看练习答案与解析
答案:
if (a[j] < a[j + 1])(将>改为<即可实现降序排列) 解析:冒泡排序的升序和降序只取决于比较符号的方向。升序用>(大的往后移),降序用<(小的往后移)。
四、编程题(每小题 10 分,共20分)
- 编写一个程序,对有序数列进行二分查找。
查看答案与解析
答案:
public class BinarySearch {
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9, 11, 13, 15};
int target = 7;
int result = binarySearch(arr, target);
if (result != -1) {
System.out.println("找到目标 " + target + ",索引为 " + result);
} else {
System.out.println("未找到目标 " + target);
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
解析:
第一步:理解二分查找原理 二分查找要求数组有序。每次取中间元素与目标值比较,若相等则找到;若中间值小于目标值,说明目标在右半部分;若中间值大于目标值,说明目标在左半部分。
第二步:确定算法步骤
- 初始化左指针
left = 0,右指针right = n - 1。 - 当
left <= right时循环:- 计算中间位置
mid = left + (right - left) / 2(避免整数溢出)。 - 若
arr[mid] == target,找到目标,返回 mid。 - 若
arr[mid] < target,目标在右半部分,left = mid + 1。 - 若
arr[mid] > target,目标在左半部分,right = mid - 1。
- 计算中间位置
- 循环结束未找到,返回 -1。
第三步:分析边界条件
- 使用
left <= right而非left < right,确保单个元素也能被检查。 - 使用
mid = left + (right - left) / 2而非(left + right) / 2,防止 left + right 溢出。
难度: ⭐⭐ 考点: #二分查找
💡 学习锦囊
📖 相关公式与知识点:
- 二分查找时间复杂度:$O(\log_2 n)$,每次比较将搜索范围减半。
- 二分查找前提:数组必须有序(升序或降序)。
- mid 安全计算:
mid = left + (right - left) / 2防止整数溢出。
易错点
- 二分查找中忘记
mid ± 1,导致死循环。 - 循环条件应为
left <= right,不是left < right。
🔄 举一反三
- 编写递归版本的二分查找算法。
查看练习答案与解析
javapublic static int binarySearchRecursive(int[] arr, int target, int left, int right) { if (left > right) return -1; int mid = left + (right - left) / 2; if (arr[mid] == target) return mid; if (arr[mid] < target) return binarySearchRecursive(arr, target, mid + 1, right); else return binarySearchRecursive(arr, target, left, mid - 1); }1
2
3
4
5
6
7
8
9解析:递归版本将循环改为函数自调用,基准条件是
left > right时返回 -1。递归深度为 $O(\log n)$。
- 编写一个servlet,实现对文件名中包含中文的文件进行下载的功能,并写出其配置代码。
查看答案与解析
答案:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.URLEncoder;
@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String fileName = request.getParameter("filename");
if (fileName == null || fileName.trim().isEmpty()) {
response.getWriter().write("文件名不能为空");
return;
}
String filePath = getServletContext().getRealPath("/files/") + File.separator + fileName;
File file = new File(filePath);
if (!file.exists()) {
response.getWriter().write("文件不存在");
return;
}
response.setContentType(getServletContext().getMimeType(fileName));
response.setContentLengthLong(file.length());
String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
.replaceAll("\\+", "%20");
response.setHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + encodedFileName);
try (FileInputStream fis = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
web.xml 配置(或使用 @WebServlet 注解):
<servlet>
<servlet-name>FileDownloadServlet</servlet-name>
<servlet-class>FileDownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileDownloadServlet</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>2
3
4
5
6
7
8
解析:
第一步:理解中文文件名下载的核心问题 HTTP 响应头中直接包含中文字符会导致乱码,因为 HTTP 头默认只支持 ASCII 字符。需要对中文文件名进行 URL 编码。
第二步:关键处理步骤
- 使用
URLEncoder.encode(fileName, "UTF-8")对文件名进行编码。 - 将编码后的
+替换为%20(空格的正确编码)。 - 设置
Content-Disposition头,使用filename*=UTF-8''格式(RFC 5987)来支持中文文件名。 - 设置正确的
Content-Type和Content-Length。 - 使用文件输入流读取文件,通过响应输出流写入客户端。
第三步:配置说明
- 使用
@WebServlet("/download")注解或 web.xml 配置 Servlet 映射。 - 文件存放在 Web 应用的
/files/目录下。
难度: ⭐⭐ 考点: #Servlet #文件下载 #中文编码 #URLEncoder #Content-Disposition
💡 学习锦囊
📖 相关公式与知识点:
- Servlet 文件下载核心步骤:
- 设置
Content-Type(MIME 类型)。 - 设置
Content-Disposition: attachment告诉浏览器下载而非直接打开。 - 将文件内容写入
response.getOutputStream()。
- 设置
- 中文文件名处理:
URLEncoder.encode()+ RFC 5987 格式filename*=UTF-8''。
易错点
- 文件下载后不关闭流,导致资源泄露(使用 try-with-resources)。
- 中文文件名直接用
filename="中文.pdf"会导致乱码,必须使用 RFC 5987 编码格式。
🔄 举一反三
- 如何在 Servlet 中实现文件上传功能?
查看练习答案与解析
答案:使用
@MultipartConfig注解标记 Servlet,通过request.getPart("fileFieldName")获取上传的文件 Part 对象,再调用part.write(filePath)保存到服务器。需要在表单中设置enctype="multipart/form-data"。